package com.kob.consumer.utils;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.api.R;
import com.kob.Mapper.RecordMapper;
import com.kob.consumer.WebSocketServer;
import com.kob.pojo.Bot;
import com.kob.pojo.Record;
import com.kob.pojo.User;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.SocketUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;

public class Game extends Thread{
    private final Integer rows,cols;
    private final Integer inner_walls_count;
    private final int [][] g;
    private final static int[] dx={-1,0,1,0},dy={0,1,0,-1};
    private final Player playerA,playerB;
    private Integer nextStepA = null;
    private Integer nextStepB = null;
    private ReentrantLock lock = new ReentrantLock();
    private String status = "playing";  //finished
    private String loser = "";
    private final static String addBotUrl = "http://127.0.0.1:3002/bot/add/";


    public void setNextStepA(Integer nextStepA) {
        lock.lock();
        try{
            this.nextStepA = nextStepA;
        }finally {
            lock.unlock();
        }

    }

    public void setNextStepB(Integer nextStepB) {
        lock.lock();
        try{
            this.nextStepB = nextStepB;
        }finally {
            lock.unlock();
        }
    }

    public Player getPlayerA() {
        return playerA;
    }

    public Player getPlayerB() {
        return playerB;
    }

    public Game(Integer rows, Integer cols, Integer inner_walls_count, Integer idA, Bot botA , Integer idB, Bot botB){
        this.cols=cols;
        this.inner_walls_count=inner_walls_count;
        this.rows=rows;
        this.g=new int[rows][cols];
        Integer botIdA = -1,botIdB =-1;
        String botCodeA = "" , botCodeB = "";
        if(botA!=null){
            botIdA = botA.getId();
            botCodeA=botA.getContent();
        }
        if(botB!=null){
            botIdB = botB.getId();
            botCodeB=botB.getContent();
        }
        playerA = new Player(idA,botIdA,botCodeA,rows-2,1,new ArrayList<Integer>());
        playerB = new Player(idB,botIdB,botCodeB,1,cols-2,new ArrayList<Integer>());

    }
    public int[][] getG(){
        return g;
    }

    private  boolean check_connectivity(int sx,int sy,int tx,int ty){
        if (sx == tx && sy == ty) return true;
        g[sx][sy] = 1;

        for (int i = 0; i < 4; i++) {
            int x = sx + dx[i], y = sy + dy[i];
            if (g[x][y]==0 && x>=0&&x<this.rows&&y>=0&&y<this.cols){
                if(this.check_connectivity(x, y, tx, ty)){
                    g[sx][sy]=0;
                    return true;
                }
            }
        }
        return false;
    }
    private boolean draw(){
        for(int i=0;i<this.rows;i++){
            for(int j=0;j<cols;j++){
                g[i][j]=0;
            }
        }
        for(int r=0;r<this.rows;r++){
            g[r][0]=g[r][this.cols-1]=1;
        }
        for(int c=0;c<this.cols;c++){
            g[0][c]=g[this.rows-1][c]=1;
        }
        Random random = new Random();
        for(int i=0;i<this.inner_walls_count/2;i++){
            for(int j=0;j<1000;j++){
                int r= random.nextInt(this.rows);
                int c= random.nextInt(this.cols);
                if(g[r][c]==1 || g[this.rows-1-r][this.cols-1-c]==1)
                    continue;
                if(r==this.rows-2 && c==1 || r==1 && c==this.cols-2)
                    continue;
                g[r][c]=g[this.rows-1-r][this.cols-1-c]=1;
                break;
            }
        }
        return check_connectivity(this.rows-2,1,1,this.cols-2);
    }
    public void createMap(){
        for(int i=0;i<1000;i++){
            if(draw()) break;
        }
    }
    private String getInput(Player player){  //当前局面信息编码成字符串
        Player me,you;
        if(playerA.getId().equals(player.getId())){
            me = playerA;
            you = playerB;
        }else{
            me = playerB;
            you = playerA;
        }
        return getMapString()+"#"+
                me.getSx()+"#"+
                me.getSy()+"#("+
                me.getStepsString()+")#"+
                you.getSx()+"#"+
                you.getSy()+"#("+
                you.getStepsString()+")";
    }

    private void sendBotCode(Player player){
        if(player.getBotId().equals(-1)) return;
        MultiValueMap<String,String> data = new LinkedMultiValueMap<>();
        data.add("user_id",player.getId().toString());
        data.add("bot_code",player.getBotCode());
        data.add("input",getInput(player));
        WebSocketServer.restTemplate.postForObject(addBotUrl,data,String.class);
    }

    private boolean nextStep() throws Exception {
        Thread.sleep(200);
        sendBotCode(playerA);
        sendBotCode(playerB);
        for(int i=0;i<50;i++){
            Thread.sleep(100);
            lock.lock();
            try{
                if(nextStepA!=null&&nextStepB!=null){
                    playerA.getSteps().add(nextStepA);
                    playerB.getSteps().add(nextStepB);
                    return true;
                }
            }finally {
                lock.unlock();
            }
        }
        return false;
    }
    private boolean checkA_valid(List<Cell> cellsA,List<Cell> cellsB){
        int n=cellsA.size();
        Cell cell = cellsA.get(n-1);
        if(g[cell.x][cell.y]==1) return false;

        for(int i=0;i<n-1;i++){
            if(cellsB.get(i).x ==cell.x &&cellsB.get(i).y==cell.y){
                return false;
            }
        }
        return true;
    }
    private boolean checkB_valid(List<Cell> cellsB,List<Cell> cellsA){
        int n=cellsB.size();
        Cell cell = cellsB.get(n-1);
        if(g[cell.x][cell.y]==1) return false;

        for(int i=0;i<n-1;i++){
            if(cellsA.get(i).x ==cell.x &&cellsA.get(i).y==cell.y){
                return false;
            }
        }
        return true;
    }

     private void judge(){ //判断玩家下一步操作是否合法
         List<Cell> cellsA = playerA.getCells();
         List<Cell> cellsB = playerB.getCells();

         boolean VaidA = checkA_valid(cellsA,cellsB);
         boolean VaidB = checkB_valid(cellsB,cellsA);

         if(!VaidA || !VaidB){
             status="finished";
             if(!VaidA && !VaidB){
                 loser="all";
             }else if(!VaidA){
                 loser="A";
             }else {
                 loser="B";
             }
         }
     }
    private void sendAllMessage(String message){
        if(WebSocketServer.users.get(playerA.getId())!=null)
            WebSocketServer.users.get(playerA.getId()).sendMessage(message);
        if(WebSocketServer.users.get(playerB.getId())!=null)
            WebSocketServer.users.get(playerB.getId()).sendMessage(message);

    }

    private void sendMove(){ //向玩家传递移动信息
        lock.lock();
        try{
            JSONObject resp = new JSONObject();
            resp.put("event","move");
            resp.put("a_direction",nextStepA);
            resp.put("b_direction",nextStepB);
            sendAllMessage(resp.toJSONString());
            nextStepA = nextStepB = null;
        }finally {
            lock.unlock();
        }

    }

    private void sendResult(){ //向两个玩家公布结果
        JSONObject resp = new JSONObject();
        resp.put("event","result");
        resp.put("loser",loser);
        saveToDatabase();
        sendAllMessage(resp.toJSONString());
    }

    private String getMapString(){
        StringBuilder res = new StringBuilder();
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                res.append(g[i][j]);
            }
        }
        return res.toString();
    }
    private void updateUserRating(Player player,Integer rating){
        User user = WebSocketServer.userMapper.selectById(player.getId());
        user.setRating(rating);
        WebSocketServer.userMapper.updateById(user);
    }

    private void saveToDatabase(){
        Integer ratingA = WebSocketServer.userMapper.selectById(playerA.getId()).getRating();
        Integer ratingB = WebSocketServer.userMapper.selectById(playerB.getId()).getRating();
        if("A".equals(loser)){
            ratingA -= 2;
            ratingB += 5;
        }else{
            ratingA += 5;
            ratingB -= 2;
        }
        updateUserRating(playerA,ratingA);
        updateUserRating(playerB,ratingB);

        Record record = new Record(
              null,
              playerA.getId(),
              playerA.getSx(),
              playerA.getSy(),
              playerB.getId(),
              playerB.getSx(),
              playerB.getSy(),
                playerA.getStepsString(),
                playerB.getStepsString(),
                getMapString(),
                loser,
                new Date()
        );
        WebSocketServer.recordMapper.insert(record);
    }


    @SneakyThrows
    public void run() {
        for(int i=0;i<1000;i++){
                if(nextStep()){
                    judge();
                    if(status.equals("playing")){
                        sendMove();
                    }else {
                        sendResult();
                        break;
                    }
                }else{
                    status = "finished";
                    lock.lock();
                    try{
                        if(nextStepA==null&&nextStepB==null){
                            loser = "all";
                        }else if(nextStepA==null){
                            loser = "A";
                        }else{
                            loser="B";
                        }
                    }finally {
                        lock.unlock();
                    }
                    sendResult();
                    break;
                }
            }
        }
}
